package control;
import java.util.Stack;

import domain.Category;
import domain.Expense;
import domain.FamilyMember;
import domain.Income;
import domain.Loan;
import domain.PaymentMethod;

import basic.Manager;

import storage.ManageData;
import view.SidePane;

import interfaces.CommandLine;

//@author A0098891H
public class Shell implements CommandLine {
	
	public final static int EXPENSE = 1;
	public final static int INCOME = 2;
	public final static int LOAN = 3;
	public final static int CATEGORY = 4;
	public final static int MEMBER = 5;
	public final static int PAYMENT_METHOD = 6;
	public final static int BUDGET = 7;

	public final static int SEARCH = 8;
	public final static int REPORT = 9;
	public final static int ALERT = 10;
	public final static int LIST = 11;
	public final static int UNDO = 13;
	public final static int REDO = 14;
	public final static int UPDATE = 15;

	public final static int VIEW_MEMBER = 20;
	public final static int VIEW_CATEGORY = 21;
	public final static int VIEW_PAYMETHOD = 22;
	public final static int VIEW_EXPENSE = 23;
	public final static int VIEW_INCOME = 24;
	public final static int VIEW_LOAN = 25;
	public final static int VIEW_ALERT = 26;
	public final static int VIEW_BUDGET = 27;
	
	//error message
	public final static int ERR_ARG1 = -1;

	private Stack<Integer> _undoStack;
	private Stack<Integer> _redoStack;
	private Stack<String> _cmdStackUp;
	private Stack<String> _cmdStackDown;
	private BudgetManager _budgetMan;
	private ExpenseManager _expenseMan;
	private IncomeManager _incomeMan;
	private LoanManager _loanMan;
	private MemberManager _memMan;
	private CategoryManager _catMan;
	private PaymentMethodManager _payMan;
	private Alert _alert;
	private SidePane _sidePane;
	
	/**
	 * @param budgetMan
	 * @param expenseMan
	 * @param incomeMan
	 * @param loanMan
	 * @param memMan
	 * @param catMan
	 * @param payMan
	 */
	public Shell(Alert alert, BudgetManager budgetMan, ExpenseManager expenseMan, IncomeManager incomeMan,
	LoanManager loanMan, MemberManager memMan, CategoryManager catMan, PaymentMethodManager payMan, SidePane sidePane)
	{
		this.initialize();
		_alert = alert;
		_budgetMan = budgetMan;
		_expenseMan = expenseMan;
		_incomeMan = incomeMan;
		_loanMan = loanMan;
		_memMan = memMan;
		_catMan = catMan;
		_payMan = payMan;
		_sidePane = sidePane;
		
		_cmdStackUp = new Stack<String>();
		_cmdStackDown = new Stack<String>();
	}
	/**
	 * 
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	public boolean initialize()
	{
		if(loadAll()==false)
		{
			_undoStack = new Stack<Integer>();
			_redoStack = new Stack<Integer>();
		}
		return true;
	}
	/**
	 * 
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	public boolean saveAll()
	{
		return ManageData.saveShellToFile(this);
	}
	
	/**
	 * 
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	public boolean loadAll()
	{
//		Shell sh  = ManageData.loadShellFromFile();
//		if(sh==null)
//		{
			return false;
//		}
//		set(sh);
//		return true;
	}
	
	/**
	 * 
	 * @param sh
	 * @Precondition 
	 * @Postcondition 
	 */
//	private void set(Shell sh)
//	{
//		this.undoStack = sh.undoStack;
//		this.redoStack = sh.redoStack;
//	}
	
	
	public String upCmd()
	{
		if(!_cmdStackUp.isEmpty())
		{
			String cmd = _cmdStackUp.pop();
			_cmdStackDown.push(cmd);
			return cmd;
		}
		return null;
	}
	
	public String downCmd()
	{
		if(!_cmdStackDown.isEmpty())
		{
			String cmd = _cmdStackDown.pop();
			_cmdStackUp.push(cmd);
			return cmd;
		}
		return null;
	}
	/**
	 * 
	 * @param managerID
	 * @param cmd
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	public int sendCmd(int managerID, int shAction, String cmd)
	{
		int action;
		String strAction = "";
		if(shAction==Shell.UNDO)
		{
			strAction = "UNDO";
		}
		else if(shAction==Shell.REDO)
		{
			strAction = "REDO";
		}
		switch(managerID)
		{
		case EXPENSE:
			action = _expenseMan.readCmd(cmd);
			switch(action)
			{
			case Manager.ACTION_ADD:
				_sidePane.appendRecent(strAction + " add Expense \""+((Expense)_expenseMan.get_returned()).get_name()+"\"\n");
				break;
			case Manager.ACTION_EDIT:
				_sidePane.appendRecent(strAction + " edit Expense \""+((Expense)_expenseMan.get_returned()).get_name()+"\"\n");
				break;
			case Manager.ACTION_DELETE:
				_sidePane.appendRecent(strAction + " delete Expense \""+((Expense)_expenseMan.get_returned()).get_name()+"\"\n");
				break;
			}
			_budgetMan.updateBudget();
			return 0;
		case INCOME:
			action = _incomeMan.readCmd(cmd);
			switch(action)
			{
			case Manager.ACTION_ADD:
				_sidePane.appendRecent(strAction + " add Income \""+((Income)_incomeMan.get_returned()).get_name()+"\"\n");
				break;
			case Manager.ACTION_EDIT:
				_sidePane.appendRecent(strAction + " edit Expense \""+((Income)_incomeMan.get_returned()).get_name()+"\"\n");
				break;
			case Manager.ACTION_DELETE:
				_sidePane.appendRecent(strAction + " delete Expense \""+((Income)_incomeMan.get_returned()).get_name()+"\"\n");
				break;
			}
			_budgetMan.updateBudget();
			return 0;
		case LOAN:
			action = _loanMan.readCmd(cmd);
			switch(action)
			{
			case Manager.ACTION_ADD:
				_sidePane.appendRecent(strAction + " add Loan \""+((Loan)_loanMan.get_returned()).get_name()+"\"\n");
				break;
			case Manager.ACTION_EDIT:
				_sidePane.appendRecent(strAction + " edit Loan \""+((Loan)_loanMan.get_returned()).get_name()+"\"\n");
				break;
			case Manager.ACTION_DELETE:
				_sidePane.appendRecent(strAction + " delete Loan \""+((Loan)_loanMan.get_returned()).get_name()+"\"\n");
				break;
			}
			_budgetMan.updateBudget();
			return 0;
		case BUDGET:
			_budgetMan.readCmd(cmd);
			return 0;
		case MEMBER:
			action = _memMan.readCmd(cmd);
			switch(action)
			{
			case Manager.ACTION_ADD:
				_sidePane.appendRecent(strAction + " add member \""+((FamilyMember)_memMan.get_returned()).get_name()+"\"\n");
				break;
			case Manager.ACTION_EDIT:
				_sidePane.appendRecent(strAction + " edit member \""+((FamilyMember)_memMan.get_returned()).get_name()+"\"\n");
				break;
			case Manager.ACTION_DELETE:
				_sidePane.appendRecent(strAction + " delete member \""+((FamilyMember)_memMan.get_returned()).get_name()+"\"\n");
				break;
			}
			return 0;
		case CATEGORY:
			action = _catMan.readCmd(cmd);
			switch(action)
			{
			case Manager.ACTION_ADD:
				_sidePane.appendRecent(strAction + " add category \""+((Category)_catMan.get_returned()).get_name()+"\"\n");
				break;
			case Manager.ACTION_EDIT:
				_sidePane.appendRecent(strAction + " edit category \""+((Category)_catMan.get_returned()).get_name()+"\"\n");
				break;
			case Manager.ACTION_DELETE:
				_sidePane.appendRecent(strAction + " delete category \""+((Category)_catMan.get_returned()).get_name()+"\"\n");
				break;
			}
			return 0;
		case PAYMENT_METHOD:
			action = _payMan.readCmd(cmd);
			switch(action)
			{
			case Manager.ACTION_ADD:
				_sidePane.appendRecent(strAction + " add payment method \""+((PaymentMethod)_payMan.get_returned()).get_name()+"\"\n");
				break;
			case Manager.ACTION_EDIT:
				_sidePane.appendRecent(strAction + " edit payment method \""+((PaymentMethod)_payMan.get_returned()).get_name()+"\"\n");
				break;
			case Manager.ACTION_DELETE:
				_sidePane.appendRecent(strAction + " delete payment method \""+((PaymentMethod)_payMan.get_returned()).get_name()+"\"\n");
				break;
			}
			return 0;
		default:
			return -1;
		}
	}
	/**
	 *
	 * @see interfaces.CommandLine#readCmd(java.lang.String)
	 * @param cmd
	 * @return
	 * @Precondition 
	 * @Postcondition 
	 */
	@Override
	public int readCmd(String cmd) {
		_cmdStackUp.add(cmd);

		int index = cmd.indexOf(",");
		if(index<0)
			index = cmd.length();
		String str = cmd.substring(0,index);
		int res = -1;
		int action = -1;

		switch(str.toLowerCase())
		{
		case "hi":
		case "hello":
			_sidePane.appendRecent("Hello.\n   Welcome to EzXpns.\n");
			return 0;
		case "clear":
			_sidePane.clear();
			return 0;
		case "view cat":
		case "view category":
			_sidePane.appendRecent("View Category:\n"+_catMan.toString());
			return VIEW_CATEGORY;

		case "view mem":
		case "view member":
			_sidePane.appendRecent("View Member:\n"+_memMan.toString());
			return VIEW_MEMBER;

		case "view pay":
		case "view payment method":
			_sidePane.appendRecent("View Payment Method:\n"+_payMan.toString());
			return VIEW_PAYMETHOD;

		case "view expense":
			_sidePane.appendRecent("View Expense:\n"+_expenseMan.toString());
			return VIEW_EXPENSE;
			

		case "view income":
			_sidePane.appendRecent("View Income:\n"+_incomeMan.toString());
			return VIEW_INCOME;
			

		case "view loan":
			_sidePane.appendRecent("View Loan & Investment:\n"+_loanMan.toString());
			return VIEW_LOAN;

		case "view alert":
			_sidePane.appendRecent("View Alert Settings:\n"+_alert.toString());
			return VIEW_ALERT;
			
		case "view budget":
			_sidePane.appendRecent("View Budget Settings:\n"+_budgetMan.toString());
			return VIEW_BUDGET;
			
		case "expense":
			res = EXPENSE;
			action = _expenseMan.readCmd(cmd);
			switch(action)
			{
			case Manager.ACTION_ADD:
				_sidePane.appendRecent("Expense \""+((Expense)_expenseMan.get_returned()).get_name()+"\" is added\n");
				clearRedo();
				break;
			case Manager.ACTION_EDIT:
				_sidePane.appendRecent("Expense \""+((Expense)_expenseMan.get_returned()).get_name()+"\" is edited\n");
				clearRedo();
				break;
			case Manager.ACTION_DELETE:
				_sidePane.appendRecent("Expense \""+((Expense)_expenseMan.get_returned()).get_name()+"\" is deleted\n");
				clearRedo();
				break;
			default:
				break;
			}
			_budgetMan.updateBudget();
			break;
		case "income":
			res = INCOME;
			action = _incomeMan.readCmd(cmd);
			switch(action)
			{
			case Manager.ACTION_ADD:
				_sidePane.appendRecent("Income \""+((Income)_incomeMan.get_returned()).get_name()+"\" is added\n");
				clearRedo();
				break;
			case Manager.ACTION_EDIT:
				_sidePane.appendRecent("Income \""+((Income)_incomeMan.get_returned()).get_name()+"\" is edited\n");
				clearRedo();
				break;
			case Manager.ACTION_DELETE:
				_sidePane.appendRecent("Income \""+((Income)_incomeMan.get_returned()).get_name()+"\" is deleted\n");
				clearRedo();
				break;
			default:
				break;
			}
			_budgetMan.updateBudget();
			break;
		case "loan":
			res = LOAN;
			action = _loanMan.readCmd(cmd);
			switch(action)
			{
			case Manager.ACTION_ADD:
				_sidePane.appendRecent("Loan \""+((Loan)_loanMan.get_returned()).get_name()+"\" is added\n");
				clearRedo();
				break;
			case Manager.ACTION_EDIT:
				_sidePane.appendRecent("Loan \""+((Loan)_loanMan.get_returned()).get_name()+"\" is edited\n");
				clearRedo();
				break;
			case Manager.ACTION_DELETE:
				_sidePane.appendRecent("Loan \""+((Loan)_loanMan.get_returned()).get_name()+"\" is deleted\n");
				clearRedo();
				break;
			default:
				break;
			}
			_budgetMan.updateBudget();
			break;
		case "budget":
			res = BUDGET;
			//_budgetMan.readCmd(cmd);
			return 0;
		case "category":
			res = CATEGORY;
			action = _catMan.readCmd(cmd);
			switch(action)
			{
			case Manager.ACTION_ADD:
				_sidePane.appendRecent("Category \""+((Category)_catMan.get_returned()).get_name()+"\" is added\n");
				clearRedo();
				break;
			case Manager.ACTION_EDIT:
				_sidePane.appendRecent("Category \""+((Category)_catMan.get_returned()).get_name()+"\" is edited\n");
				clearRedo();
				break;
			case Manager.ACTION_DELETE:
				_sidePane.appendRecent("Category \""+((Category)_catMan.get_returned()).get_name()+"\" is deleted\n");
				clearRedo();
			default:
				break;
			}
			break;
		case "member":
			res = MEMBER;
			action = _memMan.readCmd(cmd);
			switch(action)
			{
			case Manager.ACTION_ADD:
				_sidePane.appendRecent("Member \""+((FamilyMember)_memMan.get_returned()).get_name()+"\" is added\n");
				clearRedo();
				break;
			case Manager.ACTION_EDIT:
				_sidePane.appendRecent("Member \""+((FamilyMember)_memMan.get_returned()).get_name()+"\" is edited\n");
				clearRedo();
				break;
			case Manager.ACTION_DELETE:
				_sidePane.appendRecent("Member \""+((FamilyMember)_memMan.get_returned()).get_name()+"\" is deleted\n");
				clearRedo();
			default:
				break;
			}
			break;
		case "payment":
		case "payment method":
			res = PAYMENT_METHOD;
			action = _payMan.readCmd(cmd);
			switch(action)
			{
			case Manager.ACTION_ADD:
				_sidePane.appendRecent("Payment Method \""+((PaymentMethod)_payMan.get_returned()).get_name()+"\" is added\n");
				clearRedo();
				break;
			case Manager.ACTION_EDIT:
				_sidePane.appendRecent("Payment Method \""+((PaymentMethod)_payMan.get_returned()).get_name()+"\" is edited\n");
				clearRedo();
				break;
			case Manager.ACTION_DELETE:
				_sidePane.appendRecent("Payment Method \""+((PaymentMethod)_payMan.get_returned()).get_name()+"\" is deleted\n");
				clearRedo();
			default:
				break;
			}
			break;
		case "undo":
			res = UNDO;
			if(!_undoStack.isEmpty())
			{
				res =  _undoStack.pop();
				_redoStack.add(res);
				if(sendCmd(res, UNDO, cmd)!=-1)
					return res;
				else return -1;
			}
			else 
			{
				_sidePane.appendRecent("NO MORE UNDO\n");
				return 0;//No more undo
			}
		case "redo":
			//TODO
			res = REDO;
			if(!_redoStack.isEmpty())
			{
				res =  _redoStack.pop();
				_undoStack.add(res);
				if(sendCmd(res, REDO, cmd)!=-1)
					return res;
				else return -1;
			}
			else 
			{
				_sidePane.appendRecent("NO MORE REDO\n");
				return 0;//No more undo
			}
		case "list":
			//TODO
			res = LIST;
			return 0;
		case "search":
			//TODO
			res = SEARCH;
			return 0;
		case "report":
			//TODO
			res = REPORT;
			return 0;
		case "alert":
			//TODO
			res = ALERT;
			return 0;
		default:
			_sidePane.appendRecent("Invalid Command. \n Press F1 to view the help.\n");
			return ERR_ARG1;
		}
		
		_undoStack.add(res);
		if(action==-1)
		{
			switch(res)
			{
			case EXPENSE:
				_sidePane.appendRecent("Expense: \n");
				break;
			case INCOME:
				_sidePane.appendRecent("Income: \n");
				break;
			case LOAN:
				_sidePane.appendRecent("Loan: \n");
				break;
			case MEMBER:
				_sidePane.appendRecent("Member: \n");
				break;
			case CATEGORY:
				_sidePane.appendRecent("Category: \n");
				break;
			case VIEW_PAYMETHOD:
				_sidePane.appendRecent("Payment Method: \n");
				break;
			}
			res = -1;
			_sidePane.appendRecent("Syntax Error.\n");
		}
		return res;
	}
	
	public void clearRedo()
	{
		if(!_redoStack.isEmpty())
		{
			_redoStack.clear();
		}
	}
}
